#!/usr/bin/python

import sys
import time

from apscheduler.triggers.cron import CronTrigger
from apscheduler.triggers.interval import IntervalTrigger
from apscheduler.triggers.date import DateTrigger

from system.core.exception import (
    DatabaseException,
    ModuleNotFoundException,
    ClassNotFoundException,
    )

"""
Class       : ServiceFactory
Description : A Class which invoke specified module name and class
"""

class ServiceFactory(object):

    """
    create_service_instance: A class method to create service instance by module name and class name and arguments
    """
    @classmethod
    def create_service_instance(cls, module_name, class_name, arguments = None):
        try:
            class_obj = getattr(sys.modules[module_name], class_name)
            if arguments is not None:
                instance = class_obj(arguments = arguments)
            else:
                instance = class_obj()  
        except ImportError,e:
            raise ModuleNotFoundException("[Service][create_service_instance]Can not find module %s" % (module_name))
        except AttributeError,e:
            raise ClassNotFoundException("[Service][create_service_instance]Can not find class %s from module %s" % (class_name, module_name))
        return instance


class APSchedTriggerBaseException(Exception):

    """
    class       : APschedTriggerBaseException
    description : define trigger base exception
    """

class APSchedTriggerTypeException(APSchedTriggerBaseException):

    """
    class       : APSchedTriggerTypeException
    description : define trigger type exception, available type is 'interval', 'crontab' and 'datetime'
    """

class APSchedTriggerArgsException(APSchedTriggerBaseException):

    """
    class       : APSchedTriggerArgsException
    description : define trigger arguments exception
    """

"""
Class      : ApschedTriggerFactory
Descrption : apscheduler trigger factory
"""

class APSchedTriggerFactory():

    """
    create_trigger_instance: create apscheduler trigger instance by trigger type and trigger arguments
    """
    @classmethod
    def create_trigger_instance(cls, trigger_type, trigger_args):
        type = trigger_type.lower()
        trigger_type_list = ['crontab', 'interval', 'datetime']
        if type not in trigger_type_list:
            raise APSChedTriggerTypeException("Trigger type is not in trigger type list['crontab', 'interval', 'datetime']")
        else:
            if trigger_args is None:
                raise APschedTriggerArgsException("Trigger arguments can not be None")
            else:
                class_obj         = None
                trigger_key_list  = []
                trigger_arguments = {}

                """
                get trigger class and trigger key list
                """
                if type == 'crontab':
                    class_obj = CronTrigger
                    trigger_key_list = ['year', 'month', 'day', 'week', 'day_of_week', 'hour', 'minute', 'second', 'start_date', 'end_date', 'timezone']
                elif type == 'interval':
                    class_obj        = IntervalTrigger
                    trigger_key_list = ['weeks', 'days', 'hours', 'minutes', 'seconds', 'start_date', 'end_date', 'timezone']
                elif type == 'datetime':
                    class_obj        = DateTrigger
                    trigger_key_list = ['run_date', 'timezone']

                """
                validate trigger arguments
                """
                validate_result = cls._validate_trigger_args(trigger_key_list, trigger_args)
                if validate_result["result"] == False:
                    raise APSchedTriggerArgsException("Trigger arguments without key %s" % (validate_result["key"]))

                if cls._is_none_args(trigger_args):
                    raise APSchedTriggerArgsException("Trigger arguments is all none value")

                """
                create trigger new arguments by old arguments
                """
                if type == 'crontab' or type == 'datetime':
                    trigger_arguments = cls._create_crontab_and_date_trigger_args(trigger_key_list, trigger_args)
                elif type == 'interval':
                    trigger_arguments = cls._create_interval_trigger_args(trigger_key_list, trigger_args)

                """
                return trigger new instance
                """
                return class_obj(**trigger_arguments)
        return None

    """
    _validate_trigger_args: class method to validate trigger arguments
    """
    @classmethod
    def _validate_trigger_args(cls, trigger_key_list, trigger_args):
        for key in trigger_key_list:
            if not trigger_args.has_key(key):
                return {"result":False, "key":key}
        return {"result":True, "key":""}


    """
    _is_none_args: check if all arguments is none
    """
    @classmethod
    def _is_none_args(cls, trigger_args):
        is_all_none = True
        for key in trigger_args:
            if trigger_args[key] is not None and trigger_args[key] != 0 and trigger_args[key] != '':
                is_all_none = False
                break;
        return is_all_none

    """
    _create_crontab_trigger_args: create crontab and date trigger arguments
    """
    @classmethod
    def _create_crontab_and_date_trigger_args(cls, trigger_key_list, trigger_args):
        args = {}
        for key in trigger_key_list:
            if trigger_args[key] == '' or trigger_args[key] == None:
                args[key] = None
            else:
                args[key] = trigger_args[key]
        return args

    """
    _create_interval_trigger_args: create interval trigger arguments
    """
    @classmethod
    def _create_interval_trigger_args(cls, trigger_key_list, trigger_args):
        args = {}
        spec_key_list = ['start_date', 'end_date', 'timezone']
        for key in trigger_key_list:
            if trigger_args[key] == '' or trigger_args[key] == None:
                if key in spec_key_list:
                    args[key] = None
                else:
                    args[key] = 0
            else:
                args[key] = trigger_args[key]
        return args


"""
Class       : ServiceInvoker
Description : service invoker
"""
class ServiceInvoker():
    @staticmethod
    def invoke(args = None, id = None, task_name = None, module_name = None, class_name = None, arguments = None):
        instance = None
        try:
            instance = ServiceFactory.create_service_instance(module_name, class_name, arguments = arguments)
            instance.invoke()
            instance.release_resource();
        except DatabaseException,e:
            print (e)
        except ModuleNotFoundException,e:
            print (e)
        except ClassNotFoundException,e:
            print (e)
        finally:
            if instance is not None:
                del instance
        return
